#include "meta.h"
#include "../coding/coding.h"
#include "../util/endianness.h"

#define EA_BLOCKID_HEADER           0x5343486C /* "SCHl" */

#define EA_BLOCKID_LOC_HEADER       0x53480000 /* "SH" */

#define EA_BLOCKID_LOC_EN           0x0000454E /* English */
#define EA_BLOCKID_LOC_FR           0x00004652 /* French */
#define EA_BLOCKID_LOC_GE           0x00004745 /* German, older */
#define EA_BLOCKID_LOC_DE           0x00004445 /* German, newer */
#define EA_BLOCKID_LOC_IT           0x00004954 /* Italian */
#define EA_BLOCKID_LOC_SP           0x00005350 /* Castilian Spanish, older */
#define EA_BLOCKID_LOC_ES           0x00004553 /* Castilian Spanish, newer */
#define EA_BLOCKID_LOC_MX           0x00004D58 /* Mexican Spanish */
#define EA_BLOCKID_LOC_RU           0x00005255 /* Russian */
#define EA_BLOCKID_LOC_JA           0x00004A41 /* Japanese, older */
#define EA_BLOCKID_LOC_JP           0x00004A50 /* Japanese, newer */
#define EA_BLOCKID_LOC_PL           0x0000504C /* Polish */
#define EA_BLOCKID_LOC_BR           0x00004252 /* Brazilian Portuguese */

/* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */
VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) {

    /* check extension */
    /* they don't seem enforced by EA's tools but usually:
     * .asf: ~early (audio stream file?) [ex. Need for Speed II (PC)]
     * .lasf: fake for plugins
     * .str: ~early [ex. FIFA 98 (PS1), FIFA 2002 (PS1)]
     * .chk: ~early [ex. NBA Live 98 (PS1)]
     * .eam: ~mid?
     * .exa: ~mid [ex. 007 - From Russia with Love]
     * .sng: ~late (FIFA games)
     * .aud: ~late [ex. FIFA 14 (3DS)]
     * .strm: MySims Kingdom (Wii)
     * .stm: FIFA 12 (3DS)
     * .sx: FIFA 98 (SAT)
     * .xa: ?
     * .hab: GoldenEye - Rogue Agent (inside .big)
     * .xsf: 007 - Agent Under Fire (Xbox)
     * .gsf: 007 - Everything or Nothing (GC)
     * (extensionless): SSX (PS2) (inside .big)
     * .r: The Sims 2: Pets (PSP) (not l/r, shorter "res")
     * .snd: Command & Conquer 3: Tiberum Sun (PC) videos */
    if (!check_extensions(sf, "asf,lasf,str,chk,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,,r,snd"))
        return NULL;

    /* check header */
    uint32_t header = read_u32be(0x00, sf);
    if (header != EA_BLOCKID_HEADER &&                           /* "SCHl" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_EN) && /* "SHEN" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_FR) && /* "SHFR" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_GE) && /* "SHGE" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_DE) && /* "SHDE" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_IT) && /* "SHIT" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_SP) && /* "SHSP" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_ES) && /* "SHES" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_MX) && /* "SHMX" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_RU) && /* "SHRU" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JA) && /* "SHJA" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JP) && /* "SHJP" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_PL) && /* "SHPL" */
        header != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_BR))   /* "SHBR" */
        return NULL;

    /* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end.
     * Video uses picture blocks (MVhd/MV0K/etc) and sometimes multiaudio blocks (SHxx/SCxx/SDxx/SExx where xx=language).
     * The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */
    return load_vgmstream_ea_schl(sf, 0x00);
}

/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
VGMSTREAM* init_vgmstream_ea_bnk(STREAMFILE* sf) {
    int target_stream = sf->stream_index;

    /* check extension */
    /* .bnk: common
     * .sdt: Harry Potter games, Burnout games (PSP)
     * .hdt/ldt: Burnout games (PSP)
     * .abk: GoldenEye - Rogue Agent
     * .ast: FIFA 2004 (inside .big)
     * .cat: FIFA 2000 (PC, chant.cat)
     * (extensionless): The Sims 2 spinoffs (PSP) */
    if (!check_extensions(sf, "bnk,sdt,hdt,ldt,abk,ast,cat,"))
        return NULL;

    if (target_stream == 0) target_stream = 1;
    return load_vgmstream_ea_bnk(sf, 0x00, target_stream - 1, 0);
}

/* EA SCHl inside non-demuxed videos, used in current gen games too */
VGMSTREAM* init_vgmstream_ea_schl_video(STREAMFILE* sf) {
    VGMSTREAM* vgmstream = NULL;
    off_t offset = 0, start_offset = 0;
    int blocks_done = 0;
    int total_subsongs, target_subsong = sf->stream_index;
    read_u32_t read_u32;


    /* checks */
    /* .uv: early */
    /* .dct: early-mid [ex. Need for Speed II SE (PC), FIFA 98 (PC)] */
    /* .wve: early-mid [Madden NFL 99 (PC)] */
    /* .mad: mid */
    /* .vp6: late */
    /* .mpc: SSX Tricky (PS2) */
    if (is_id32be(0x00, sf, "SCHl")) {
        if (!check_extensions(sf, "uv,dct,mpc,lmpc,vp6"))
            return NULL;
    }
    else if (is_id32be(0x00, sf, "MADk")) {
        if (!check_extensions(sf, "mad,wve"))
            return NULL;
    }
    else if (is_id32be(0x00, sf, "MVhd")) {
        if (!check_extensions(sf, "vp6"))
            return NULL;
    }
    else if (is_id32be(0x00, sf, "MPCh")) {
        if (!check_extensions(sf, "mpc,lmpc"))
            return NULL;
    }
    else {
        return NULL;
    }

    /* use block size to check endianness */
    read_u32 = guess_endian32(0x04, sf) ? read_u32be : read_u32le;

    /* find starting valid header for the parser */
    while (offset < get_streamfile_size(sf)) {
        uint32_t block_id = read_u32be(offset + 0x00, sf);
        uint32_t block_size = read_u32(offset + 0x04, sf);

        /* find "SCHl" or "SHxx" blocks */
        if ((block_id == EA_BLOCKID_HEADER) || ((block_id & 0xFFFF0000) == EA_BLOCKID_LOC_HEADER)) {
            start_offset = offset;
            break;
        }

        if (block_size == 0xFFFFFFFF)
            goto fail;
        if (blocks_done > 10)
            goto fail; /* unlikely to contain music */

        blocks_done++;
        offset += block_size;
    }

    if (offset >= get_streamfile_size(sf))
        goto fail;

    /* find target subsong (one per each SHxx multilang block) */
    total_subsongs = 1;
    if (target_subsong == 0) target_subsong = 1;
    offset = start_offset;
    while (offset < get_streamfile_size(sf)) {
        uint32_t block_id = read_u32be(offset + 0x00, sf);
        uint32_t block_size = read_u32(offset + 0x04, sf);

        /* no more subsongs (assumes all SHxx headers go together) */
        if (((block_id & 0xFFFF0000) != EA_BLOCKID_LOC_HEADER)) {
            break;
        }

        if (target_subsong == total_subsongs) {
            start_offset = offset;
            /* keep counting subsongs */
        }

        total_subsongs++;
        offset += block_size;
    }

    if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;

    vgmstream = load_vgmstream_ea_schl(sf, start_offset);
    if (!vgmstream) goto fail;

    vgmstream->num_streams = total_subsongs;
    return vgmstream;

fail:
    close_vgmstream(vgmstream);
    return NULL;
}


/* EA standalone PT variable header - rare, found during the transition from fixed header */
VGMSTREAM* init_vgmstream_ea_pt(STREAMFILE* sf) {
    VGMSTREAM* vgmstream = NULL;
    STREAMFILE* sf_body = NULL;
    off_t head_offset, body_offset;
    size_t head_size;
    int is_split = 0;


    /* .pth: split [NBA Live 97 (PC)]
     * .dat: joined [NBA Live 97/98 (PC)] */
    /* often also found as nameless file pairs in bigfiles [FIFA 97 (PC)] */
    if (check_extensions(sf, "pth")) {
        sf_body = open_streamfile_by_ext(sf, "ptd");
        if (!sf_body) goto fail;
        is_split = 1;
    }
    else //if (!check_extensions(sf, "dat,ldat"))
        return NULL;


    if (is_split) {
        head_size = get_streamfile_size(sf);
        head_offset = 0x00;
        body_offset = 0x00;
    }
    /* these contain multiple subsongs, but with no clear way
     * to get each of their offsets, unimplemented for now */
    //else { /* NBA 97 variant, NBA 98 has an even weirder variant */
    //    head_size = read_u32le(0x00, sf);
    //    head_offset = 0x04;
    //    body_offset = head_offset + head_size;
    //    sf_body = sf;
    //}

    if (!is_id32be(head_offset, sf, "PT\0\0")) /* does standalone GSTR also exist? */
        goto fail;

    vgmstream = load_vgmstream_ea_pt(sf, sf_body, head_offset, head_size, body_offset);
    if (!vgmstream) goto fail;


    if (is_split) close_streamfile(sf_body);
    return vgmstream;

fail:
    if (is_split) close_streamfile(sf_body);
    close_vgmstream(vgmstream);
    return NULL;
}
